package com.superiron.scaffold.core.service.aspect;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.TimeInterval;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * @ClassName RedisLockAspect
 * @Author yzg
 * @Date 2022年6月13日14:32:37
 * @Description redis分布式锁拦截器实现
 **/
@Aspect
@Component
@Slf4j
public class DistributedLockAspect {

	@Autowired
	private RedissonClient redissonClient;
	
	@Pointcut("@annotation(com.superiron.scaffold.core.service.aspect.DistributedLock)")
	public void distributedLock() {
	}
	
	@Around("distributedLock()")
	public Object lockAroundAction(ProceedingJoinPoint proceeding) throws Throwable {
		Object object = null;
		RLock lock = null;


		DistributedLock distributedLock = getRlockInfo(proceeding);
		String lockKey = getLocalKey(proceeding, distributedLock);
		log.info("红锁加锁 start ：{}", lockKey);

		try {
			lock = redissonClient.getLock(lockKey);

			if (lock != null) {
				TimeInterval timer = DateUtil.timer();
				final boolean status = lock.tryLock(2, distributedLock.releaseTime(), distributedLock.timeUnit());
				log.info("加锁号：{}, 加锁状态：{}, 加锁时间：{}", lockKey, status, timer.intervalRestart());
				if (status) {
					object = proceeding.proceed();
				}
			} else {
				log.info("未获取到锁：{}", lockKey);
			}
		} finally {
			// 当前线程获取到锁再释放锁
			if (lock != null && lock.isHeldByCurrentThread()) {
				log.info("程序结束，释放锁：{}", lockKey);
				lock.unlock();
			}
		}
		return object;

	}

	public DistributedLock getRlockInfo(ProceedingJoinPoint proceedingJoinPoint) {
		MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature();
		return methodSignature.getMethod().getAnnotation(DistributedLock.class);
	}

	/**
	 * 获取redis lock key
	 *
	 * @param proceedingJoinPoint
	 * @return
	 */
	public String getLocalKey(ProceedingJoinPoint proceedingJoinPoint, DistributedLock rlockInfo) {
		final Object[] args = proceedingJoinPoint.getArgs();
		String businessNo = "";

		// 如果没有设置锁值
		if (StringUtils.isNotEmpty(rlockInfo.lockName())) {
			businessNo = rlockInfo.lockName();
		} else {
			MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature();
			Class[] parameters = methodSignature.getParameterTypes();
			String methodName = methodSignature.getMethod().getName();

			if (parameters != null) {
				for (int i = 0; i < parameters.length; i++) {
					Class parameter = parameters[i];
					if (parameter.getSimpleName().equals("RedLockKey")) {
						RedLockKey redLockKey = (RedLockKey) args[i];
						businessNo = redLockKey.getKey();
					}
				}
				// 如果没有获取到业务编号，则使用方法签名
				if (StringUtils.isEmpty(businessNo)) {
					businessNo = methodName;
				}
			}
		}
		return businessNo;
	}
}